//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <core.h>
#include <testhlp.h>
#include "physmem.h"

//====================================================
// Description  : Print the status of the memory regions
// Parameters   : None
// Return       : None
//====================================================
EXTERN void TestRegions(const char *szTitle, \
    const MemoryRegion regions[], uint_t uNumberOfRegions)
{
    kprintf("%s: Total %d Regions\n", szTitle, uNumberOfRegions);
    for (uint_t i = 0; i < uNumberOfRegions; i++) {
        kprintf("    Region [%d]: Page [%x, %x)\n", \
            regions[i].uBasePageNo, regions[i].uLimitPageNo);
    }
}

//====================================================
// Description  : Print the status of the memory holes
// Parameters   : None
// Return       : None
//====================================================
EXTERN void TestHoles()
{
    kprintf("Totol: %d Holes\n", bsp.uNumberOfMemoryHoles);
    for (uint_t j = 0; j < bsp.uNumberOfMemoryHoles; j++) {
        kprintf("    Hole [%d]: Page [0x%x, 0x%x)\n", j, \
            bsp.pMemoryHoles[j].uBasePageNo,
            bsp.pMemoryHoles[j].uLimitPageNo);
    }
}

//====================================================
// Description  : Print the status of the memory zones
// Parameters   : None
// Return       : None
//====================================================
EXTERN void TestZones()
{
    kprintf("Totol: %d Zones\n", bsp.uNumberOfMemoryZones);
    for (uint_t i = 0; i < bsp.uNumberOfMemoryZones; i++) {
        kprintf("    Zone [%d]: Page [0x%x, 0x%x)\n", i, \
            bsp.pMemoryZones[i].uBasePageNo,
            bsp.pMemoryZones[i].uLimitPageNo);
    }
}

STATIC bool_t ExistBlockInList(Zone *pZone, uint_t uBlockPageNo,
    uint_t uOrder)
{
    assert(pZone);
    for (Page *pPage = (Page *)pZone->m_buddyLists[uOrder].m_listHead.First();
        (Page *)&pZone->m_buddyLists[uOrder].m_listHead != pPage;
        pPage = (Page *)pPage->Next()) {
        if (uBlockPageNo == (uint_t)(pPage - g_pPages)) return TRUE;
    }

    return FALSE;
}

STATIC uint_t TestBuddyList(Zone *pZone, uint_t uOrder)
{
    kprintf("        Zone [%d] Buddy List: ", uOrder);

    uint_t uBasePageNo = pZone->m_region.uBasePageNo;
    uint_t uNumberOfFreePages = 0;
    uint_t unCount = 0;
    for (Page *pPage =
        (Page *)pZone->m_buddyLists[uOrder].m_listHead.First(); \
        (Page *)&pZone->m_buddyLists[uOrder].m_listHead != pPage; \
        pPage = (Page *)pPage->Next(), uNumberOfFreePages += 1 << uOrder) {

        uint_t uPageNo       = pPage - g_pPages;
        uint_t uOffsetPageNo = uPageNo - uBasePageNo;
        uint_t uPageNoMask   = (~0u) << uOrder;
        if ((uOffsetPageNo & uPageNoMask) != uOffsetPageNo) {
            kprintf("!!!ERROR!!! Invalid block (BlockPageNo: 0x%x)\n",
                uPageNo);
        }
        else {
            if (!(unCount % 5) && unCount) {
                kprintf("\n                             ");
            }
            unCount ++;
            kprintf("[0x%x] ", uPageNo);
        }

        if (pPage->m_uFlags & PageFlag_Reserved) {
            kprintf(
                "!!!ERROR!!! Reserved page No.0x%x in buddy list [%d]\n", \
                uPageNo, uOrder);
        }
    }
    kprintf("\n");

    if (c_uNumberOfOrders - 1 == uOrder) return uNumberOfFreePages;

    uint_t uNumberOfZonePages = \
        pZone->m_region.uLimitPageNo - pZone->m_region.uBasePageNo;

    for (uint_t uBuddyBlockNo = 0; \
        uBuddyBlockNo < (uNumberOfZonePages >> (uOrder + 1)); \
        uBuddyBlockNo++) {
        uint_t uBlock1PageNo = uBasePageNo + \
            (uBuddyBlockNo << (uOrder + 1));
        uint_t uBlock2PageNo = uBasePageNo + \
            (uBuddyBlockNo << (uOrder + 1)) + (1 << uOrder);

        bool_t bExist1 = ExistBlockInList(pZone, uBlock1PageNo, uOrder);
        bool_t bExist2 = ExistBlockInList(pZone, uBlock2PageNo, uOrder);

        if (bExist1 && bExist2) {
            kprintf("!!!ERROR!!! "
                "Invalid buddy blocks exist (BlockPageNo: 0x%x, 0x%x)\n", \
                uBlock1PageNo, uBlock2PageNo);
        }

        if (0 == TestBit(uBuddyBlockNo,
            pZone->m_buddyLists[uOrder].m_pbMap)) {
            if (bExist1) {
                kprintf("!!!ERROR!!! "
                    "Buddy block 1 exist (BlockPageNo: 0x%x)\n", \
                    uBlock1PageNo);
            }

            if (bExist2) {
                kprintf("!!!ERROR!!! "
                    "Buddy block 2 exist (BlockPageNo: 0x%x)\n", \
                    uBlock2PageNo);
            }
        }
        else {
            if (!bExist1 && !bExist2) {
                kprintf("!!!ERROR!!! Invalid buddy blocks not exist"
                    "(BlockPageNo: 0x%x, 0x%x)\n", \
                    uBlock1PageNo, uBlock2PageNo);
            }
        }
    }

    return uNumberOfFreePages;
}

STATIC void TestBuddyZone(Zone *pZone)
{
    uint_t uZoneNo = pZone - g_zones;
    kprintf(
        "    Zone[%d]: Page [%x, %x), Approx Max = %d, 0x%x Free Pages\n", \
        uZoneNo, \
        pZone->m_region.uBasePageNo, pZone->m_region.uLimitPageNo, \
        pZone->m_nApproxMaxOrder, pZone->m_uNumberOfFreePages);

    uint_t uNumberOfFreePages = 0;
    for (uint_t i = 0; i < c_uNumberOfOrders; i++) {
        uNumberOfFreePages += TestBuddyList(pZone, i);
    }

    if (uNumberOfFreePages != pZone->m_uNumberOfFreePages) {
        kprintf("!!!ERROR!!! "
            "Zone [%d]: Invalid Free Pages(Expected 0x%x, Actual 0x%x)\n", \
            uZoneNo, pZone->m_uNumberOfFreePages, uNumberOfFreePages);
    }
}

//====================================================
// Description  : Print the status of the buddy system
// Parameters   : None
// Return       : None
//====================================================
EXTERN DECL_CODEINIT void TestBuddySystem()
{
    kprintf("Buddy System: Total %d Zones, Max = %d\n", \
        bsp.uNumberOfMemoryZones, c_uNumberOfOrders - 1);

    for (uint_t i = 0; i < bsp.uNumberOfMemoryZones; i++) {
        TestBuddyZone(&g_zones[i]);
    }
}
